/* 
Copyright (c) 2009 
Copyright (c) 2008 Chumby Industries

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import com.gskinner.events.GDispatcher;
import com.chumby.util.Delegate;
import com.chumby.widgets.photostrip.Photo;
import com.chumby.util.MCUtil;

class com.chumby.widgets.photostrip.Photostrip extends MovieClip
{
	private static var InitialDelayTime:Number = 4000; // ms
	private static var DelayTime:Number = 4000; // ms
	private static var AlignTime:Number = 500; // ms
	private static var AdvanceTime:Number = 1000; // ms
	
	private var _xspacing:Number;
	private var _imageWidth:Number;
	private var _imageHeight:Number;
	
	public  var _isStopped:Boolean; // used to tell images when it's safe to load
	private var _imageCount:Number;
	private var _delay:Number;
	private var _startTime:Number;
	private var _startX:Number;
	private var _startXMouse:Number;
	private var _deltaX:Number;
	private var _velocity:Number;
	private var _lastPhoto:MovieClip;
	private var _duration:Number;
	private var _continuous:Boolean;
	private var _autoAdvance:Boolean;
	private var _photoSymbol:String;
	private var _images:Array;
	private var _lastCheckLoadPos:Number;
	
	public static var LOADEDFIRST:Number = 1;
	public static var MOVING:Number = 2;
	public static var STOPPED:Number = 3;
	public static var LOADED:Number = 4;
	
	public var addEventListener:Function;
	public var removeEventListener:Function;
	private var dispatchEvent:Function;
	private var _numLoading:Number = 0;
	private var _numLoaded:Number = 0;
	private var _delegateFirstImageReady:Function;
	private var _delegateImgLoaded:Function;
	
	//-----------------------------------------------------------------------------------------------
	function Photostrip()
	{
		_isStopped = true;
		_continuous = true;
		_autoAdvance = true;
		_xspacing = 320;
		_imageWidth = 320;
		_imageHeight = 240;
		GDispatcher.initialize(this);
		_delegateFirstImageReady = Delegate.create(this, firstImageReady);
		_delegateImgLoaded = Delegate.create(this, imgLoaded);
	}

	//-----------------------------------------------------------------------------------------------
	public function setAutoAdvance(isAuto:Boolean)
	{
		_autoAdvance = isAuto;
	}
		
	public function setImageSpacing(w:Number)
	{
		_xspacing = w;
	}
	
	public function setImageSize(w:Number, h:Number)
	{
		_imageWidth = w;
		_imageHeight = h;
	}
		
	//-----------------------------------------------------------------------------------------------
	// attach the movie clips for each image, space them out, then start the autoadvancer
	public function processImages(imageFiles:Array, showingIndex:Number) 
	{

		if(showingIndex == undefined) {
			showingIndex = 0;
		}
		if(imageFiles.length > 1)
		{
			this.onPress = mover;	// when initially touched, switch to the manual mover
			this.onRelease = this.onReleaseOutside = aligner;	// when touch released, switch to the aligner	
			this.onEnterFrame = advancer;
			
			// ** continuous/circular mode
			var first:String = imageFiles[0];
			var last:String = imageFiles[imageFiles.length - 1];
			imageFiles.unshift(last);
			imageFiles.push(first);
			//_x = -_xspacing;  
			_x = -_xspacing * (showingIndex + 1);
		}
		_imageCount = imageFiles.length;  // count including duplicate first and last images
		_images = new Array();
		trace("_imageCount : " + _imageCount);
		
		var x:Number = 0;
		for (var i=0; i<_imageCount; i++) 
		{
			var imagePath:String = imageFiles[i].path;
			var imageType:String = imageFiles[i].type;
			trace(imageType);
			var imageName:String = imageFiles[i].name;
			var noteContents:String = imageFiles[i].contents;
			var mc_name:String = "photo_"+i;
			var img:Photo = MCUtil.CreateWithClass(Photo, this, mc_name, (i*5) + 100,
				{_x:x, imagePath:imagePath, imageType:imageType, imageName:imageName, noteContents:noteContents,
				 _maxWidth:_imageWidth, _maxHeight:_imageHeight});
			_images.push(img);
			img.addEventListener(Photo.LOADED, _delegateImgLoaded);
			if (_imageCount == 1) { // first image when only 1 image in array
				img.addEventListener(Photo.LOADED, _delegateFirstImageReady);
			}
			else if(i == 1) {  // first visible image
				img.addEventListener(Photo.LOADED, _delegateFirstImageReady);
			}
			if(i==1 || i==_imageCount-2) {
				img.keepLoaded();
			}
			
			x += _xspacing;
			_lastPhoto = this[mc_name];
		}
		checkPhotoLoad();
		_delay = InitialDelayTime;
		_startTime = getTimer();		
	}

	//-----------------------------------------------------------------------------------------------
	public function removeAll()
	{
		trace(this._name + " removing " + _imageCount + " images");
		for (var i=0; i<_imageCount; i++) 
		{
			var mc_name:String = "photo_"+i;
			var img:MovieClip = this[mc_name];
			img.removeEventListener(Photo.LOADED, _delegateFirstImageReady);
			img.removeEventListener(Photo.LOADED, _delegateImgLoaded);
			img.removeMovieClip();
		}
		_images = new Array();
		_imageCount = 0;
	}
	
	//-----------------------------------------------------------------------------------------------	
	private function imgLoaded(obj:Object) 
	{
		var i:Number = Number(obj.name.split('_')[1]);
		var index:Number = (i==0) ? (_imageCount - 1) : (i-1);
		//trace("imgLoaded " + i);
		dispatchEvent({type:LOADED, index:i});
	}

	//-----------------------------------------------------------------------------------------------
	public function setShowingIndex(index:Number)
	{
		trace("setShowingIndex " + index);
		_x = -_xspacing * (index + 1);
		_startTime = getTimer();
		checkPhotoLoad();
		if(this._autoAdvance) {
			this.onEnterFrame = advancer;
		}
		else {
			delete this.onEnterFrame;
		}
	}
	
	//-----------------------------------------------------------------------------------------------
	public function getCurrentIndex():Number
	{
		var imageIndex:Number = Math.round(-_x/_xspacing); // current image
		return imageIndex - 1;   // subtract the duplicated image
	}
	
	//-----------------------------------------------------------------------------------------------
	function firstImageReady(evnt:Object)
	{
		trace("firstImageReady " + evnt.target);
		dispatchEvent({type:LOADEDFIRST});
	}
	
	//-----------------------------------------------------------------------------------------------
	// t=time, b=start, c=delta, d=duration
	private function easeOutQuad(t, b, c, d) 
	{
		return -c *(t/=d)*(t-2) + b;
	}

	//-----------------------------------------------------------------------------------------------
	private function easeInOutQuad(t, b, c, d) 
	{
		if ((t/=d/2) < 1) 
			return c/2*t*t + b;
		else
			return -c/2 * ((--t)*(t-2) - 1) + b;
	}

	//-----------------------------------------------------------------------------------------------
	// initialize the manual mover, keeping track of the initial press
	private function mover() 
	{
		//trace('mover');
		_startX = _x;
		_startXMouse = this._parent._xmouse;
		var x:Number = _x;
		var imageIndex:Number = Math.round( -x / _xspacing); // figure out which image we're on
		
		if (_startXMouse > 100 && _startXMouse < 220 && _images[imageIndex].isPlayable()) {
			trace('middleclick' + _images[imageIndex].isPlayable());
			_images[imageIndex].toggleAssetPlaying();
		}else {
			trace('outerclick' +' ' + imageIndex + ' ' + _images[imageIndex].isPlayable());
_images[imageIndex].stopAudioFile();
			this.onEnterFrame = move;
			_isStopped = false;
			move();
		}
		
	}

	//-----------------------------------------------------------------------------------------------
	// move the images to track the user's pointer
	private function move() 
	{
		//trace('move');
		var nowXMouse = this._parent._xmouse;
		var x:Number = _startX+(nowXMouse-_startXMouse);
		x = Math.max(Math.min(x,0),_xspacing-(_lastPhoto._x+_lastPhoto._width));
		if (x!=_x) {
			dispatchEvent({type:MOVING});
			_velocity = x-_x;// record the velocity
			_x = x;
			if(Math.abs(nowXMouse-_lastCheckLoadPos) > _xspacing) {
				checkPhotoLoad();
			}
		}
	}

	//-----------------------------------------------------------------------------------------------
	private function checkPhotoLoad()
	{
		_lastCheckLoadPos = this._parent._xmouse;
		var curr:Number = Math.round(-_x/_xspacing); // current image index
		//trace("checkPhotoLoad " + curr);
		var delta:Number = Math.min(15, Math.floor(_images.length / 2));
		_images[curr].checkLoad();
		for(var d:Number = 1; d <= delta; d++) 
		{
			var pos1:Number = (curr + d) % _images.length;
			var pos2:Number = (curr + _images.length - d) % _images.length;
			//trace("checkLoad: " + pos1 + " , " + pos2);
			_images[pos1].checkLoad();
			_images[pos2].checkLoad();
		}
	}
	
	//-----------------------------------------------------------------------------------------------
	// align the images to the nearest image boundary, taking into account how fast the
	// user was moving the images when the mouse was released
	private function aligner() 
	{
		//trace('aligner');
		var x:Number = _x;
		var image = int(-x/_xspacing+0.5); // figure out closest image to which to align
		var remaining = image*_xspacing+x;
		if (_velocity>10 && remaining>0) {
			image--;
		} else if (_velocity<-10 && remaining<0) {
			image++;
		}
		image = Math.max(0,Math.min(image,_imageCount-1));
		_velocity = 0;
		_startTime = getTimer();
		_duration = AlignTime;
		_startX = x;
		_deltaX = -image*_xspacing-_startX;
		this.onEnterFrame = align;
		align();
	}

	//-----------------------------------------------------------------------------------------------
	private function align() 
	{
		var t = getTimer() - _startTime;
		//trace('align '+t);
		if (t<=_duration) 
		{
			var x = easeOutQuad(t,_startX,_deltaX,_duration);
			if (x!=_x) {
				_x = x;
			}
		} 
		else 
		{
			var imageIndex:Number = Math.round(-_x/_xspacing);
			//trace("align index = " + imageIndex);
			if(_continuous && imageIndex == _imageCount-1) {
				//trace("wrap");
				_x = -_xspacing;
			}
			else if(_continuous && imageIndex == 0) {
				//trace("wrap");
				_x = -(_imageCount-2) * _xspacing;
			}
			else {
				_x = _startX+_deltaX; // done with this advance, force alignment
			}
			_delay = DelayTime;
			_startTime = getTimer();
			_isStopped = true;
			checkPhotoLoad();
			dispatchEvent({type:STOPPED});
			this.onEnterFrame = advancer;
		}
	}

	//-----------------------------------------------------------------------------------------------
	// advance to the next image after the specified delay
	private function advancer() 
	{
		//trace('advancer '+delay);
		if (_autoAdvance == true && getTimer() > _startTime+_delay) 
		{
			var x:Number = _x;
			var imageIndex:Number = Math.round(-x/_xspacing); // current image
			if (_continuous || (imageIndex < _imageCount-1))  // any images left?
			{ 
				_startTime = getTimer(); // set up advance
				_duration = AdvanceTime;
				_startX = _x;
				_deltaX = -_xspacing;
				this.onEnterFrame = advance;
				dispatchEvent({type:MOVING});
				_isStopped = false;
				advance();
			} 
			else 
			{
				delete this.onEnterFrame; // no more images
			}
		}
	}

	//-----------------------------------------------------------------------------------------------
	private function advance() 
	{
		//trace('advance '+t);
		var t = getTimer() - _startTime;
		if (t <= _duration) 
		{
			var x = easeInOutQuad(t,_startX,_deltaX,_duration);
			if (x!=_x) {
				_x = x;
			}
		} 
		else 
		{			
			var imageIndex:Number = Math.round(-_x/_xspacing);
			//trace("advance index = " + imageIndex);
			if(_continuous && imageIndex == _imageCount-1) {
				//trace("wrap");
				_x = -_xspacing;
			}
			else {
				_x = _startX+_deltaX; // doen with this advance, force alignment
			}
			_delay = DelayTime; // set up next advance
			_startTime = getTimer();
			_isStopped = true;
			checkPhotoLoad();
			this.onEnterFrame = advancer;
			dispatchEvent({type:STOPPED});
		}
	}
}